﻿using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer.ConnectionStrings;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESG.Entities;
using Sustainalytics.EsgRatings.BusinessLogic;
using Sustainalytics.ESGRatings.BusinessLogic;
using Sustainalytics.ESGRatings.BusinessLogic.Exceptions;
using Sustainalytics.Utils;
using Sustainalytics.Utils.Configuration;
using System;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Web.Http;
using System.Web.Http.Cors;

namespace Sustainalytics.ESGRatings.Service.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/company")]
    [LogControllerFilter(true, true, false)]
    public class ESGCompanyReportController : ApiController
    {
        private readonly string _clientPlatformConnectionString;

        private readonly string _reportConnectionString;

        private readonly IPdfReportExecutor _reportExecutor;

        private readonly IRepositoryCollection<ESGProfile> _repositoryEsgProfile;

        public ESGCompanyReportController()
        {
            _reportExecutor = PdfReportExecutorFactory.GetInstance();

            _clientPlatformConnectionString = ConfigurationManager.ConnectionStrings["ClientPlatform"].ConnectionString;

            var connectionStringsManager =
                ConnectionStringsManager.GetInstance(
                    ConnectionStringsRepository.GetInstance(_clientPlatformConnectionString));

            _reportConnectionString = connectionStringsManager.GetConnectionString(
                ConnectionStringType.CompanyReportPDF);
            _repositoryEsgProfile = new RepositoryCollection<ESGProfile>(_clientPlatformConnectionString);
        }

        public ESGCompanyReportController(IPdfReportExecutor reportExecutor)
        {
            _reportExecutor = reportExecutor;
        }

        [Route("{companyId}/esgCompanyReport")]
        public HttpResponseMessage Post(EsgCompanyReportParameters parameters)
        {
            try
            {
                if (!parameters.HasComprehensiveResearch)
                {
                    var result = TryGetReportFromFileSystemV2(parameters);
                    return Request.CreateResponse(HttpStatusCode.OK, result);
                }

                if (CheckParametersMatchDefaultProfile(parameters))
                {
                    var result = TryGetReportFromFileSystemV2(parameters);
                    return Request.CreateResponse(HttpStatusCode.OK, result);
                }

                if (WeHaveESGResearchForMatrix(parameters))
                {
                    var hash =
                        GetMd5Hash(
                            string.Format(
                                "{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}",
                                parameters.AccountId,
                                parameters.EsgProfileId,
                                parameters.CompanyId,
                                parameters.MatrixId,
                                parameters.ReferenceUniverseId,
                                parameters.TemplateType,
                                parameters.CompanyType,
                                parameters.ComparativeGroup));

                    var hashedFileName = parameters.FileName.Replace(parameters.CompanyId.ToString(), hash);
                    var reportType = GetReportType(parameters);

                    var remotePath = this.GetPathForFile(reportType);

                    var result = _reportExecutor.ExecuteEsgCompanyReport(parameters);

                    var fileHandler = new S3FileHandler(remotePath, hashedFileName, SettingsHelper.Read("s3bucketCustom", "dev-globalaccess-europe-custom"));

                    PdfReportExecutionResult file = fileHandler.WriteFile(result.FileContent);


                    file.FileName = GetFileName(parameters);
                    file.CompanyId = parameters.CompanyId;
                    CompanyReportSerilog.Log.CustomCompanyReportExecution(
                        result.ExecutionLog[0],
                        result.ExecutionLog[1],
                        result.ExecutionLog[2],
                        result.ExecutionLog[3]);
                    return Request.CreateResponse(HttpStatusCode.OK, file);
                }

                CompanyReportSerilog.Log.CustomCompanyReportNoResearchAvailable(
                    parameters.CompanyId.ToString(),
                    parameters.MatrixId.ToString());
                return Request.CreateResponse(
                    HttpStatusCode.NotFound,
                    new EsgRatingsError
                    {
                        ErrorCode = 6002,
                        ErrorMessage = "No research available for selected company and matrix."
                    });
            }
            catch (EsgComputingServiceUrlException ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(
                    HttpStatusCode.InternalServerError,
                    new EsgRatingsError { ErrorCode = 6000, ErrorMessage = "No url for the specified account" });
            }
            catch (EsgProfileInvalidException ex)
            {
                CommonEventSource.Log.Exception(ex);
                Request.CreateResponse(
                    HttpStatusCode.BadRequest,
                    new EsgRatingsError { ErrorCode = 6000, ErrorMessage = "Invalid input parameters for account" });
            }
            catch (EsgComputingException ex)
            {
                CommonEventSource.Log.Exception(ex);
                Request.CreateResponse(
                    HttpStatusCode.InternalServerError,
                    new EsgRatingsError
                    {
                        ErrorCode = 6001,
                        ErrorMessage =
                                string.Format("Failed to generate the ESG Company Report: {0}", ex)
                    });
            }
            catch (Exception ex)
            {
                CommonEventSource.Log.Exception(ex);
                return Request.CreateResponse(
                    HttpStatusCode.InternalServerError,
                    new EsgRatingsError
                    {
                        ErrorCode = 6001,
                        ErrorMessage =
                                string.Format("Failed to generate the ESG Company Report: {0}", ex)
                    });
            }

            return Request.CreateResponse(HttpStatusCode.OK);
        }

        private static string GetFileName(EsgCompanyReportParameters parameters)
        {
            var ddmmyyyy = DateTime.UtcNow.ToString("ddMMyyyy", new CultureInfo("en-US"));
            var pos = parameters.FileName.Length - 40;

            // GUID + .pdf to be replaced with date
            var fileName = parameters.FileName.Remove(pos) + ddmmyyyy + ".pdf";

            return fileName;
        }

        private static string GetMd5Hash(string str)
        {
            // First we need to convert the string into bytes, which
            // means using a text encoder.
            Encoder enc = Encoding.Unicode.GetEncoder();

            // Create a buffer large enough to hold the string
            byte[] unicodeText = new byte[str.Length * 2];
            enc.GetBytes(str.ToCharArray(), 0, str.Length, unicodeText, 0, true);

            // Now that we have a byte array we can ask the CSP to hash it
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] result = md5.ComputeHash(unicodeText);

            // Build the final string by converting each byte
            // into hex and appending it to a StringBuilder
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            // And return it
            return sb.ToString();
        }

        private static bool WeHaveESGResearchForMatrix(EsgCompanyReportParameters parameters)
        {
            var companyId = parameters.CompanyId;
            var accountId = parameters.AccountId;
            var matrixId = parameters.MatrixId;

            var plainBL = Controversies.BusinessLogic.PlainBlFactory.GetInstance<RPESGResearchState>();

            // todo petre implement exists in repository collection
            var res = (accountId != Guid.Empty)
                          ? plainBL.ReadAllWhere(
                              x =>
                              x.CompanyId == companyId && (x.AccountId == accountId || x.AccountId == null)
                              && x.ProfileHasEsgResearch && x.MatrixId == matrixId,
                              0,
                              1)
                          : plainBL.ReadAllWhere(
                              x => x.CompanyId == companyId && x.ProfileHasEsgResearch && x.MatrixId == matrixId,
                              0,
                              1);

            if (res != null && res.Any())
            {
                var state = res.FirstOrDefault();
                if (state != null)
                {
                    return state.ProfileHasEsgResearch;
                }
            }

            return false;
        }

        private bool CheckParametersMatchDefaultProfile(EsgCompanyReportParameters parameters)
        {
            if (parameters.AccountTier == 2 || parameters.AccountTier == 1)
            {
                if (parameters.MatrixId == Guid.Empty && parameters.ReferenceUniverseId == Guid.Empty)
                {
                    return true;
                }

                var esgProfile =
                    (new RepositoryCollection<ESGProfile>(_clientPlatformConnectionString)).ReadAllWhere(
                        x => x.AccountId == null).FirstOrDefault();

                if (esgProfile == null)
                {
                    return false;
                }

                return esgProfile.MatrixId == parameters.MatrixId
                       && esgProfile.ReferenceUniverseId == parameters.ReferenceUniverseId
                       && (int)esgProfile.ComparativeGroup == parameters.ComparativeGroup
                       && (int)esgProfile.CompanyType == parameters.CompanyType
                       && (int)esgProfile.TemplateType == parameters.TemplateType;
            }

            if (parameters.AccountTier == 3)
            {
                return true;
            }

            return false;
        }

        private byte[] GeneratePdf(EsgCompanyReportParameters parameters)
        {
            if (string.IsNullOrEmpty(parameters.MatrixName))
            {
                parameters.MatrixName = "Default Weight Matrix";
            }

            var esgProfile = _repositoryEsgProfile.ReadAllWhere(a => a.AccountId == null).FirstOrDefault();

            parameters.EsgProfileId = esgProfile.Id;
            parameters.MatrixId = esgProfile.MatrixId;
            parameters.ReferenceUniverseId = esgProfile.ReferenceUniverseId;
            parameters.ReferenceUniverseName = esgProfile.ReferenceUniverseName;
            parameters.ComparativeGroup = (int)esgProfile.ComparativeGroup;
            parameters.CompanyType = (int)esgProfile.CompanyType;
            parameters.TemplateType = (int)esgProfile.TemplateType;

            var pdf = _reportExecutor.ExecuteEsgCompanyReport(parameters);

            return pdf.FileContent;
        }

        private string GetPathForFile(string reportType) => $"{reportType}_{GetDatabaseGroup()}";

        private PdfReportExecutionResult TryGetReportFromFileSystemV2(EsgCompanyReportParameters parameters)
        {
            var reportType = GetReportType(parameters);

            var remotePath = this.GetPathForFile(reportType);

            var fileHandler = new S3FileHandler(remotePath, parameters.FileName);

            PdfReportExecutionResult file;

            if (!fileHandler.FileExists())
            {
                var fileContent = this.GeneratePdf(parameters);
                file = fileHandler.WriteFile(fileContent);
            }
            else
            {
                file = fileHandler.ReadFile();
            }

            file.FileName = GetFileName(parameters);
            file.CompanyId = parameters.CompanyId;

            return file;
        }

        private string GetDatabaseGroup()
        {
            var databaseGroup = DatabaseGroup.None;

            if (_reportConnectionString.Contains("_A"))
                databaseGroup = DatabaseGroup.A;
            if (_reportConnectionString.Contains("_B"))
                databaseGroup = DatabaseGroup.B;

            return databaseGroup.ToString();
        }

        private string GetReportType(EsgCompanyReportParameters parameters)
        {
            var reportType = parameters.HasComprehensiveResearch
                ? parameters.IsSummaryReport
                    ? ReportType.SummaryComprehensiveReport
                    : ReportType.ComprehensiveReport
                : parameters.IsSummaryReport
                    ? ReportType.SummaryCoreReport
                    : ReportType.CoreReport;

            return reportType.ToString();
        }
    }
}